Zanka while I


Podražitve

1. podnaloga

Nek izdelek naj bi se vsak teden podražil za 2%. Da bi razumeli, kaj to pomeni, napiši funkcijo kolikoTednov(cena), ki za dano začetno ceno (v €) tega izdelka vrne število tednov, ko bo cena dosegla ali presegla 1000€.

V dobrih 6 letih bi se to zgodilo s kavo, ki sedaj stane 1€!

Za šaljivce, ki bodo funkcijo poklicali z negativno ceno, vrni -1

Uradna rešitev

def kolikoTednov(cena):
    ''' v koliko tednih se ob 2% tedenski podražitvi cena dvigne nad 1000€'''
    končnaCena = 1000
    trenutnaCena = cena
    tednov = 0
    if trenutnaCena < 0:
        return -1
    while trenutnaCena < končnaCena:
        trenutnaCena = trenutnaCena * 1.02
        tednov += 1 # to je isto kot tednov = tednov + 1
    return tednov

Bakterije

1. podnaloga

Bakteriolog Bine preučuje bakterije. Naredil je sledeči poizkus: bakterije je zaprl v škatlico pravokotne oblike s pregrado na sredini, ki razdeli škatlico na dva kvadratna dela - poimenujmo ju regija A in regija B. Zunanje stene te škatlice so polprepustne, kar pomeni, da bacili lahko uidejo ven, vendar se ne morejo vrniti v škatlico, pregrada pa je prepustna, tj. bacili lahko prehajajo v obe smeri. Bine predvideva, da v neki časovni enoti 12 % vseh bacilov ostane v svoji regiji, po 22 % pa jih zapusti svojo regijo v vsaki od štirih smeri. Binetu sestavi program, ki bo prebral začetni masi bacilov (bacili so premajhni, da bi jih # šteli posamično, zato jih "štejemo" kar v gramih) v vsaki od regij in izpisal število korakov, dokler skupna masa bacilov v obeh regijah ne pade pod 0.001 grama. Tako bo lahko Bine primerjal teoretični izračun z izmerjenimi podatki in preveril svojo hipotezo. Predpostavi, da so podatki pravilni (nenegativni!

Pozor! Premisli, kaj se dogaja na pregradi!

Za začetni masi 22.4 in 13.5 se število bakterij giblje

1, 5.658, 6.548
2, 2.11952, 2.03052
3, 0.7010568, 0.7099568
4, 0.240317312, 0.239427312
5, 0.08151208608, 0.08160108608
6, 0.0277336892672, 0.0277247892672
7, 0.00942749635085, 0.00942838635085
8, 0.00320554455929, 0.00320545555929
9, 0.00108986557016, 0.00108987447016
10 0.0003035929996319583 0.0003035829997319583

zato bi program izpisal 10. Primer:

 >>>Masa v regiji A: 22.4
 Masa v regiji B: 13.5
 Korakov: 10

Uradna rešitev

# Določimo število tednov, ko se ustrezno zmanjša začetna porazdelitev bakterij
masaA = float(input('Masa v regiji A: '))
masaB = float(input('Masa v regiji B: '))
korak = 0
while masaA + masaB >= 0.001: # skupna teža je še prevelika
    staraA = masaA # potrebovali bomo za izračun B-ja
    masaA = masaA * 0.12  # toliko jih ostane
    masaA = masaA + masaB * 0.22 # toliko jih pa še pride iz B
    masaB = masaB * 0.12
    masaB = masaB + staraA * 0.22
    korak += 1 # en korak več
print('Korakov:', korak)

Vsote

1. podnaloga

Sestavite funkcijo vsotaKvadratov(n), ki izračuna in vrne vsoto $1^2 + 2^2 + 3^2 + \ldots + n^2$. Primer:

>>> vsotaKvadratov(10)
385

Uradna rešitev

def vsotaKvadratov(n):
    '''Vsota kvadratov'''
    s = 0
    for i in range(1, n + 1): # začnemo pri 1 in končamo pri n
        s += i * i
    return s

def vsotaKvadratovMat(n):
    '''No, če ste malo bolj "doma" v matematiki,
       je vaša rešitev seveda '''
    return n * (n + 1) * (2*n + 1) // 6

2. podnaloga

Sestavite funkcijo vsota(n), ki izračuna in vrne vsoto $1\cdot2 + 2\cdot3 + 3\cdot4 + \ldots + n\cdot(n+1)$. Primer:

>>> vsota(10)
440

Uradna rešitev

def vsota(n):
    '''Vsota vrste '''
    s = 0
    for i in range(1, n + 1): # začnemo pri 1 in končamo pri n
        s += i * (i + 1)
    return s

3. podnaloga

Sestavite funkcijo stevilo_clenov(m), ki izračuna, največ koliko členov vsote $1\cdot2 + 2\cdot3 + 3\cdot4 + \ldots + n\cdot(n+1) + \ldots$ lahko seštejemo, da bo dobljena vsota še vedno manjša ali enaka m. Primer:

>>> stevilo_clenov(20)
3

Uradna rešitev

def stevilo_clenov(m):
    '''Koliko členov vrste lahko seštejemo, da ne presežemo meje m'''
    vsotaVrste = 0
    if m < 1: # ne smemo sešteti nobenega
        return 0
    stČlenov = 0
    while vsotaVrste <= m:
        stČlenov += 1
        vsotaVrste += stČlenov * (stČlenov + 1)
    return stČlenov - 1 # z zadnjim smo že presegli mejo

4. podnaloga

Sestavite funkcijo najblizje(a, b, m), ki poišče takšno število k med a in b, pri katerem se z eno od delnih vsot $k\cdot(k+1) + (k+1)\cdot(k+2) + (k+2)\cdot(k+3) + \ldots$ najbolj približamo številu m. Če je takšnih števil več, naj funkcija vrne najmanjšega. Primer:

>>> najblizje(10, 20, 10000)
14

Uradna rešitev

def blizina(k, m):
    '''Kako najbližje se lahko z $k\cdot(k+1) + (k+1)\cdot(k+2) + (k+2)\cdot(k+3) + \ldots$
       približamo meji m '''
    sZgoraj = 0
    while sZgoraj <= m: # nehali bomo, ko bomo presegli mejo
        sZgoraj += k * (k + 1)
        k += 1
    sSpodaj = sZgoraj - (k - 1) * k # če se ustavimo prej, še ne presežemo m
    return min(sZgoraj - m, m - sSpodaj) # ali smo se meji bolj približali od spodaj, ali od zgoraj

def najblizje(a, b, m):
    '''Kje med a in b moramo začeti sešetvati člene vrste, da se najbolj približamo meji m'''
    najmanRaz, najK = blizina(a, m), a # kandidat je a
    for k in range(a + 1, b + 1): # še preostale
        razdalja = blizina(k, m)
        if razdalja < najmanRaz: # našli smo boljšega kandidata
            najmanRaz = razdalja
            najK = k
    return najK

Čaramo s celimi števili

1. podnaloga

Sestavi funkcijo obrniStevilo(n), ki obrne pozitivno celo število. Iz 234 torej naredi število 432. Če je podatek slučajno negativen, vrni 0!

Tisti, ki že poznate funcije in metode za delo z nizi, teh prijemov tukaj ne smete uporabiti. V tej nalogi torej nikjer ne smemo uporabljati nizov!

Uradna rešitev

def obrniStevilo(n):
    ''' Funkcija obrne pozitivno celo število'''
    if n < 0:
        return 0
    obrnjeno = 0
    while n > 0:
        obrnjeno = obrnjeno * 10  + n % 10
        n = n // 10
    return obrnjeno

2. podnaloga

Najprej si oglej naslednjo funkcijo. Ugotovi, kaj počne! Očitno jo je pisal nek programer začetnik, kar lahko zlahka ugotovimo po odsotnosti komentarjev in izbiri imen spremenljivk

   def f(a, b):
       d = a % b # koliko še ostane
       k = 0
       r = '.'
       while k <=  10: 
           d = d * 10
           nd = d // b
           r = r + str(nd) 
           k = k + 1 
           d = d % b 
       return r

Namig: pokliči jo s parametri 7, 3; 2, 4; 15, 7; 1, 3; 2, 3; 3, 3 in poskusi postaviti hipotezo, kaj se dogaja ...

Oborožen z vedenjem o zgornji funkciji sestavi funkcijo kvocient(a, b, n), ki bo vrnila celi del in prvih n decimalk kvocienta a/b v obliki niza. Delaj samo s pozitivnimi celimi števili in na koncu NE zaokrožuj

   >>>kvocient(10, 761, 40)
   0.0131406044678055190538764783180026281208 
   >>>kvocient(100, 3, 14)
   33.33333333333333

Namig: kako delimo "peš"?

Uradna rešitev

def kvocient(a, b, n):
    '''Izračunamo a/b na n decimalk (za dec. piko)'''
    celiDel = a // b
    rezultat = str(celiDel) + '.'
    delimo = a % b # koliko še ostane
    decimalke = 0
    while decimalke < n: # koliko decimalk potrebujemo
        delimo = delimo * 10
        novaDec = delimo // b
        rezultat = rezultat + str(novaDec) # dodamo decimalko
        decimalke = decimalke + 1 # je ena več
        delimo = delimo % b # za naslednji korak
    return rezultat

3. podnaloga

Programer Mihec je sestavil funkcijo vsebuje(a, k), ki naj bi povedala (True/False) če poljubno celo število a vsebuje števko k.

   def vsebuje(stevilo, k):
       ''' ali število stevilo vsebuje števko k '''
       while stevilo > 0:
           if stevilo % 10 == k:
               return True
           stevilo = stevilo // 10
       return False

Preizkusil jo je na storitvi Projekt Tomo, a je kar naprej dobival obvestila o napačnem rezultatu. Preizkusi jo še ti in jo ustrezno popravi!

Uradna rešitev

def vsebuje(stevilo, k):
    ''' ali število stevilo vsebuje števko k '''
    if stevilo == k == 0 :
        return True
    stevilo = abs(stevilo) # zaradi neg. števil!
    while stevilo > 0:
        if stevilo % 10 == k:
            return True
        stevilo = stevilo // 10
    return False

4. podnaloga

Sestavi funkcijo prestejVsebuje(a,b,k), ki prešteje, koliko števil med a in b (meji vključeni) vsebuje števko k. A pozor, lahko je tudi a >= b

Uradna rešitev

def prestejVsebuje(a, b, k):
    ''' prešteje koliko je med a in b števil, ki vsebujejo števko k '''
    mini = min(a, b)
    maksi = max(a, b)
    stevilo = mini
    koliko = 0 # koliko jih zadošča pogoju
    while stevilo <= maksi:
        if vsebuje(stevilo, k):
            koliko += 1
        stevilo = stevilo + 1
    return koliko

5. podnaloga

Sestavi funkcijo prestejPrave(a,b,k), ki prešteje, koliko števil med a in b (0 <= a <= b), meji vključeni, vsebuje števko k ali pa so ona ali pa njihovi obrati deljivi s k.

Uradna rešitev

def vsebuje(stevilo, k):
    ''' ali število stevilo vsebuje števko k '''
    if stevilo == k == 0 :
        return True
    while stevilo > 0:
        if stevilo % 10 == k:
            return True
        stevilo = stevilo // 10
    return False

def obrniStevilo(n):
    ''' Funkcija obrne pozitivno celo število'''
    obrnjeno = 0
    while n > 0:
        obrnjeno = obrnjeno * 10  + n % 10
        n = n // 10
    return obrnjeno

def prestejPrave(a, b, k):
    ''' prešteje koliko je med a in b števil, ki zadoščajo pogoju k '''
    stevilo = a
    koliko = 0 # koliko jih zadošča pogoju
    while stevilo <= b:
        if vsebuje(stevilo, k) or stevilo % k == 0 or obrniStevilo(stevilo) % k == 0:
            koliko += 1
        stevilo = stevilo + 1
    return koliko

Zlati rez

Pravimo, da sta števili $a$ in $b$ v razmerju zlatega reza, kadar je $a : b$ enako $(a + b) : a$, kar je takrat, ko je $\frac{a}{b}$ enako številu $\phi = \frac{1 + \sqrt{5}}{2}$.

Približek števila $\phi$ lahko izračunamo z zaporedjem $\phi_0, \phi_1, \phi_2, \dots$, kjer je $\phi_0 = 1$, naslednji približek $\phi_{n + 1}$ pa izračunamo kot $\phi_{n + 1} = 1 + 1 / \phi_n$.

1. podnaloga

Sestavite funkcijo naslednjiPriblizek(x), ki iz približka x po zgornjem postopku izračuna naslednji približek števila $\phi$.

Uradna rešitev

def naslednjiPriblizek(x):
    '''Naslednji približek za zlati rez'''
    return 1 + 1 / x

2. podnaloga

Sestavite funkcijo priblizek(k), ki izračuna k. približek števila $\phi$. Za začetni približek (ko je k enak $0$) vzamite število $1$.

Uradna rešitev

def priblizek(k):
    '''K-ti približek za zlati rez, če začnemo z 1'''
    x = 1
    poskus = 1 #kateri člen zaporedja
    while poskus <= k:
        x = naslednjiPriblizek(x)
        poskus += 1
    return x

3. podnaloga

Sestavite funkcijo natancniPriblizek(eps), ki izračuna prvi približek števila $\phi$, ki se od prejšnjega približka razlikuje za manj kot pozitivno realno število eps.

Uradna rešitev

def natancniPriblizek(eps):
    '''Približek, ki se od prejšnjega razlikuje za manj kot eps (začnemo z 1)'''
    prejsnji = 1
    naslednji = naslednjiPriblizek(prejsnji)
    while abs(prejsnji - naslednji) >= eps:
        prejsnji = naslednji # zadnji je za naslednji korak prejšnji
        naslednji = naslednjiPriblizek(prejsnji)
    return naslednji

Kvadratni koren

Približke za kvadratni koren števila $n$ lahko izračunamo po naslednjem postopku. Začetni približek $x_0$ je enak $n / 2$. Vsak naslednji približek $x_{k + 1}$ pa izračunamo kot $(x_k + n / x_k) / 2$.

1. podnaloga

Sestavite funkcijo priblizek(n, k), ki po zgornjem postopku izračuna k. približek korena števila n.

Uradna rešitev

def priblizek(n, k):
    '''K-ti približek za kvadratni koren'''
    trenutniPrib = n / 2
    i = 0
    while i < k:
        trenutniPrib = (trenutniPrib + n / trenutniPrib) / 2
        i = i + 1
    return trenutniPrib

2. podnaloga

Sestavite funkcijo koren(n, eps), ki po zgornjem postopku izračuna prvi približek korena števila n, za katerega se kvadrat približka od n razlikuje za manj kot eps. Smislena vrednost za argument eps je npr. $10^{-6}$.

Uradna rešitev

def koren(n, eps):
    '''Približek za koren na eps natančno'''
    priblizek = n / 2
    while abs(priblizek ** 2 - n) > eps:
        priblizek = (priblizek + n / priblizek) / 2
    return priblizek